from zope.interface import implements
import os, sys
import shutil
from datetime import date
import re

APPLICATION_INSTALL_DIR = os.environ.get('SIMPLEWATCHER_INSTALL')
if not APPLICATION_INSTALL_DIR:
    raise EnvironmentError("must set environment variable SIMPLEWATCHER_INSTALL")
sys.path.append('%s/lib' % APPLICATION_INSTALL_DIR)
 
from watcher import WatcherObject
from watcher.interfaces import IRunBehavior
from watcher.action import RunBehavior
from watcher.utils import systemCommand
from watcher.exceptions import WatcherActionError


def debug(workItem, data, actionType):
    debug = data.get('debug')
    if debug and debug.lower() == 'y':
        print "DEBUG: action type is %s" % actionType
        print "DEBUG: workItem is %s" % workItem
        print "DEBUG: debugging data is %s" % data


class IExternalRunBehavior(IRunBehavior):
    """extension to standard runbehaviors.
       identical to run behavior in its implementation
       however, it will intentionally ignore the workitem
       at hand, and perform actions unrelated to the current
       workitem.
    """


class ExternalRunBehavior(RunBehavior):
    """identical to run behavior in its implementation
       however, it will intentionally ignore the workitem
       at hand, and perform actions unrelated to the current
       workitem.
    """
    implements(IExternalRunBehavior)
    def __call__(self, workItem, data):
        raise NotImplementedError("ExternalRunBehavior is abstract.  use a subclass")
    

def matchFiles(directory, pattern, ignoreCase="N"):
    """return a list of matching files"""
    matched_files = []
    if not os.path.isdir(directory):
        raise WatcherActionError("directory %s is not valid!" % directory)
    if ignoreCase.lower() == 'y':
        matcher = re.compile(pattern, re.IGNORECASE)
    else:
        matcher = re.compile(pattern)
    
    for filename in os.listdir(directory):
        if matcher.search(os.path.basename(filename)):
            matched_files.append(filename)
    return matched_files


class ClearFilesFromDirectory(ExternalRunBehavior):
    """clear a particular pattern match of files
       from a given directory"""
    def __call__(self, workItem, data):
        # generic "if we're set to debug, then print out our data" debug action
        debug(workItem, data, "clear_files_from_dir")
        # start real handling
        clearDir = data['directory']
        pattern = data.get('pattern', '.*')
        ignoreCase = data.get('ignoreCase', 'N')
        onFailure = data.get('onFailure', 'ignore')
        files_to_delete = matchFiles(clearDir, pattern, ignoreCase)
        
        for file in files_to_delete:
            try:
                os.unlink(os.path.join(clearDir, file))
            except Exception, e:
                if onFailure.lower() == 'ignore':
                    pass
                else:
                    raise WatcherActionError("could not clear file %s from directory %s!" % (file, clearDir))
        

class MoveFilesToDirectory(ExternalRunBehavior):
    """move files from one directory to another"""
    
    def __call__(self, workItem, data):
        # generic "if we're set to debug, then print out our data" debug action
        debug(workItem, data, "move_files_to_dir")
        # start real handling
        sourceDir = data['source']
        destDir = data['destination']
        pattern = data.get('pattern', '.*')
        ignoreCase = data.get('ignoreCase', 'N')
        onFailure = data.get('onFailure', 'ignore')
        files_to_move = matchFiles(sourceDir, pattern, ignoreCase)
        for file in files_to_move:
            fileWithPath = os.path.join(sourceDir, file)
            newfname = os.path.join(destDir, file)
            try:
                shutil.move(fileWithPath, newfname)
            except IOError:
                if onFailure.lower() == 'ignore':
                    pass
                else:
                    raise WatcherActionError("File %s was not able to be moved from %s to %s" % (fname, sourceDir, destDir))


class CopyFilesToDirectory(ExternalRunBehavior):
    """copy files from one directory to another"""
    def __call__(self, workItem, data):
        # generic "if we're set to debug, then print out our data" debug action
        debug(workItem, data, "copy_files_to_dir")
        # start real handling
        sourceDir = data['source']
        destDir = data['destination']
        pattern = data.get('pattern', '.*')
        ignoreCase = data.get('ignoreCase', 'N')
        onFailure = data.get('onFailure', 'ignore')
        files_to_copy = matchFiles(sourceDir, pattern, ignoreCase)
        for file in files_to_copy:
            fileWithPath = os.path.join(sourceDir, file)
            newfname = os.path.join(destDir, file)
            try:
                shutil.copyfile(fileWithPath, newfname)
            except IOError:
                if onFailure.lower() == 'ignore':
                    pass
                else:
                    raise WatcherActionError("File %s was not able to be moved from %s to %s" % (fname, sourceDir, destDir))


class CopyFilesToShare(ExternalRunBehavior):
    """copy files from a local directory to a share"""
    def __call__(self, workItem, data):
        # generic "if we're set to debug, then print out our data" debug action
        debug(workItem, data, "copy_files_to_share")
        # start real handling
        sourceDir = data['source']
        serviceName = data['service'] # //server/service
        workgroup = data['workgroup']
        credentialsConfig = data['credentials_file']
        remoteIp = data['ip_address']
        remoteDir = data.get('destination', '.')
        pattern = data.get('pattern', '.*')
        ignoreCase = data.get('ignoreCase', 'N')
        onFailure = data.get('onFailure', 'ignore')
        smbAction = u"put"
        exitAction = u"exit"
        args = u"cd %s; lcd %s; " % (unicode(remoteDir), unicode(sourceDir))
        # now we've constructed almost everything we need for the command.  now we loop and generate our commands.
        files_to_copy = matchFiles(sourceDir, pattern, ignoreCase)
        for file in files_to_copy:
            args += u" %s %s; " % (smbAction, unicode(file))
        # we have a all the files we want to put.  now we tack exit on the end, and run it.
        args += u" %s;" % exitAction
        cmd = u"smbclient %s -N -I %s -A %s -W %s -c '%s'" % (serviceName, remoteIp, credentialsConfig, workgroup, args)
        try:
            (o, e) = systemCommand(cmd)
        except Exception, err:
            e = err
        if e != '':
            if onFailure.lower() == 'ignore':
                pass
            else:
                raise WatcherActionError(e)